﻿#pragma once
#include <iostream>
#include <qobject.h>
#include <vector>
#include <memory>
#include <mutex>
#include <condition_variable>

#include "DataToImageThread.h"

#define BufferDataType		uint8_t			// 表示缓冲区数据的类型

#define ImageDefaultHeight	1080			// 表示图片默认高度
#define ImageDefaultWidth   1920			// 表示图片默认宽度
#define ImageDefaultPixel   3				// 表示图片默认采用rgb888(此时一个像素点占3字节)

#define ImageBufferDefaultSize 5			// 图片缓冲区默认容量

/*
 * 图片属性
 */
struct ImageClass
{
    ImageClass(size_t imageWidth = ImageDefaultWidth, size_t imageHeight = ImageDefaultHeight, size_t imagePixel = ImageDefaultPixel);
    size_t		     ImageHeight;		// 用于表示图片高度
    size_t			 ImageWidth;		// 用于表示图片宽度
    size_t			 ImagePixel;		// 用于表示图片一个像素所占字节数
};

/*
*	该类主要用于保存图片上传时零碎数据帧，一但凑齐一整张图片后立即将该图片上发至图片缓冲区中 这里缓冲区只存储图片数据，如果是数据报数据接收到之后立刻上传上位机并进行处理
*/
class Buffer
{
public:
    /**
     @interpret   缓冲区构造函数
     @param       imageHeight、imageWidth、imagePixel：表示当前缓冲区需要缓存图片的高、宽、单个像素点占字节数
     @return      无
     */
    Buffer(ImageClass& imageClass);
    /**
     @interpret   专门用于向缓冲区中写入单张图片数据
     @param       data：待写入数据的句柄; dataLen：写入数据的长度(并返回实际写入数据的长度); rows: 表示当前图片的行号
     @return      true-成功;false-失败;
     */
    bool write(BufferDataType* data, size_t& dataLen, size_t rows);

    /**
     @interpret   一次直接读取一整张图片
     @param       Rdata：输出型参数，图片数据句柄; dataLen：返回实际图片字节数值
     @return      无
     */
    void Read(BufferDataType* Rdata, size_t& dataLen);

    /**
     @interpret   设置缓存区中ImageClass值
     @param       无
     @return      无
    */
    void SetImageClass(ImageClass& imageClass);

    /**
     @interpret   当Image缓冲区接受完一张图片后就调用该虚函数，并唤醒对应消费者
     @param       无
     @return      无
    */
    virtual void RecviceImage(int Datalen) = 0;
protected:
    BufferDataType*  m_PBufferData;		// 用于表示缓冲区数据的句柄
    std::mutex	     m_BufferMutex;		// 针对当前缓冲区中临界资源进行保护
    std::condition_variable m_cv;		// 条件变量
private:
    size_t			 m_BufferSize;		// 用于表示缓冲区大小
    ImageClass		 m_Image;			// 用于表示当前图片设置
    size_t		     m_DataLen;		    // 用于表示缓冲区中有效数据长度
    BufferDataType* m_PBufferHead;		// 用于表示缓冲区数据的头指针
    BufferDataType* m_PBufferTail;		// 用于表示缓冲区数据的尾指针
    bool		     m_IsReceiveData;	// 用于表示缓冲区是否可以接收数据
};

/*
*	图片缓冲区，暂时设置为缓存5张图片，设置循环队列，后面更新的图片会覆盖之前的图片
*	在图片缓冲区中每个图片所占内存与前面缓冲区一致，所以在实际Buffer缓冲区上传图片给ImageBuffer缓冲区时，只需直接交换有效数据的句柄即可。
*/
class ImageBuffer : public Buffer {
public:
    /**
     @interpret   当前类的构造函数
     @param       imageBufferSize: 当前图片缓冲区的容量; imageClass: 需要缓存图片的信息
     @return      true-成功;false-失败;
     */
    ImageBuffer(size_t imageBufferSize, ImageClass& imageClass);
    ~ImageBuffer();

    /**
     @interpret   读取图片
     @param       pImage: 输出型参数,用于返回对应图片的句柄
     @return      true-成功;false-失败;
     */
    bool readImage(BufferDataType** pImage);

    /**
     @interpret   获取图片缓冲区中存有图片数量
     @param       无
     @return      返回当前缓冲区中存放图片数量
     */
    std::size_t GetImageLen();

    /**
     @interpret   设置图片缓存区中ImageClass值
     @param       无
     @return      无
     */
    void SetImageClass(ImageClass& imageClass);

    /**
     @interpret   当缓冲区Buffer存满数据后，会调用该方法（该方法是对基类BUffer的虚函数重写）
     (在该方法中是直接将PBufferData句柄与类中进行交换，所以传入的PBufferData指针也应是堆上开辟空间，且二者大小一致，不然可能会出现异常bug)
     @param       无
     @return      无
     */
    void RecviceImage(int Datalen) override;

private:
    std::size_t		m_ImageBufferSize;			// 用于表示图片缓冲区的容量
    ImageClass		m_Image;					// 用于表示当前图片设置
    std::size_t		m_ImageSize;				// 表示一张图片大小(占字节数)
    std::size_t		m_ImageHead;				// 图片缓冲区中开始获取的索引
    std::size_t		m_ImageNumber;				// 图片缓冲区中实际存储图片数量
    std::vector<BufferDataType*> m_PImageV;     // 用于管理缓存图片的容器
    DataToImageThread* m_ToImage;
};
